-
Notifications
You must be signed in to change notification settings - Fork 0
AGPHVT-82 : Improve buffer transitions and content copying between frame passes. #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Signed-off-by: Patrick Hodoul <[email protected]>
…ng the right thing.
# Conflicts: # source/engine/renderBufferManager.cpp # test/tests/testFramePasses.cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a bunch of observation comments to understand and document the PR.
You can ignore most of them, but maybe these points stand out a little more:
- Use of TkToken instead of string for
RenderBufferBinding::rendererName - Removal of
CreateRenderBufferProxyclass in framePassUtils.h/cpp- I believe the class isn't relevant anymore, since the RenderBufferManager now supports scenarios with mixed render delegates. As far as I know, the
CreateRenderBufferProxyclass was only added to support these scenarios, which is now redundant.
- I believe the class isn't relevant anymore, since the RenderBufferManager now supports scenarios with mixed render delegates. As far as I know, the
- Add check or assert to make sure the colorInput and colorDesc are for a "color" aov
(I believe there is an implicit assumption the color is always first, but just in case, I would add something to double-check- and protect this assumption against future changes.
See in RenderBufferManager.cpp (ln :558-562)
else if (!colorInput.texture)
{
colorDesc = desc;
colorInput = input;
}
| HdAovTokens->instanceId }, | ||
| {}, | ||
| _passParams.renderParams.viewport); // NOTE: this is the non-adjusted viewport. | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I never understood the exact reason why initializing AOV buffers was necessary when using a renderer other than Storm.
I can see similar code being executed with the TaskController, from the constructor:
HdxTaskController::HdxTaskController -> HdxTaskController::_CreateRenderGraph() -> HdxTaskController::SetRenderOutputs("color")
Since I do not understand why this line had been added to the TaskController, I am slightly worried removing it here might break something.
However, on the other hand, I can see that _bufferManager->SetRenderOutputs is called early, in the beggining of FramePass::GetRenderTasks. Furthermore, all task update code (the commit functions) are called at the very end of FramePass::GetRenderTasks, which means not much is happening before SetRenderOutputs is called. So maybe it does not change anything to make this call later, given the order of things with the FramePass.
In conclusion, removing _bufferManager->SetRenderOutputs from this task creation function in the FramePass is probably safe, and this call might only be needed with the TaskController because it has a different task parameter update sequence, requiring the early call to HdxTaskController::SetRenderOutputs("color").
| hvt::RenderBufferBindings FramePass::GetRenderBufferBindingsForNextPass( | ||
| std::vector<pxr::TfToken> const aovs, bool copyContents) | ||
| { | ||
| std::string renderName = GetRenderIndex()->GetRenderDelegate()->GetRendererDisplayName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| std::string renderName = GetRenderIndex()->GetRenderDelegate()->GetRendererDisplayName(); | |
| std::string const& renderName = GetRenderIndex()->GetRenderDelegate()->GetRendererDisplayName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, a TfToken would be preferable here I think. The TfToken for the rendererName could be stored in the FramePass of the RenderBufferManager in FramePass::Initialize.
| HdAovDescriptorList outputDescs; | ||
|
|
||
| // NOTE: A function could be used to get localOutputs. | ||
| TfTokenVector localOutputs = outputs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Observation]
This is out of scope for this PR since the name did not change, but we could rename localOutputs to something more descriptive of what it actually is, something like supportedOutputs maybe, since the only difference that I see between outputs and localOutputs is that each entry is validated as shown below:
for (auto it = localOutputs.begin(); it != localOutputs.end();)
{
HdAovDescriptor desc = _pRenderIndex->GetRenderDelegate()->GetDefaultAovDescriptor(*it);
if (desc.format == HdFormatInvalid)
{
// The backend doesn't support this AOV, so skip it.
it = localOutputs.erase(it);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that "outputs" is a very confusing name, as indeed it is an input to the render pass. To me, "local" is ok, or maybe better yet "temp", because they are what is being computed in this function. "Supported" is ok too and is actually what it is filtering on. I don't want to change this now though because this PR is already too big.
|
|
||
| // If progressive rendering is enabled, render buffer clear is only required when `_aovOutputs | ||
| // != outputs`. | ||
| bool needClear = !_isProgressiveRenderingEnabled || _aovOutputs != outputs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Observation]
bool needClear = !_isProgressiveRenderingEnabled || _aovOutputs != outputs;
I see this was moved below and is only executed if (somethingChanged) which should be functionally the same as before, with the early return if cached inputs and outputs have changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we just can't bail out early because we need to do the copy, but the functionality should still be skipped.
| { | ||
| std::copy(inputs.begin(), inputs.end(), back_inserter(_aovInputs)); | ||
| _viewportAov = TfToken(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this block was also moved below and is only executed if (somethingChanged), which makes sense and should deliver the same outcome as before:
_aovInputs.clear();
if (inputs.size() > 0)
{
std::copy(inputs.begin(), inputs.end(), back_inserter(_aovInputs));
_viewportAov = TfToken();
}
| } | ||
| if (colorInput.texture) | ||
| { | ||
| PrepareBuffersFromInputs(colorInput, depthInput, colorDesc, controllerId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Observation]
My understanding is this function replaces the CopyTask.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
| // Note, it would be better to just assign the output buffer here, but this breaks some | ||
| // unit tests that expect this to be null and do a pointer-as-string comparison if it is not | ||
| // which is not easily fixable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Question]
Which test is this breaking?
(If the test is TestTaskParameters, I could probaly do something about it)
In any case, this appears like a minor thing, won't block the PR for this, I am mostly curious of the test this would break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two tests that compare the task parameters. They just use the io << functionality so the pointer is written out deep in Hydra.
| HdRenderBuffer* firstInput = nullptr; | ||
| HdRenderBuffer* depthInput = nullptr; | ||
| HdRenderBuffer* neyeInput = nullptr; | ||
| for (auto input : inputs) | ||
| { | ||
| // This is super fragile and limited. | ||
| // We should be able to pass more inputs to other passes. | ||
| if (input.first == localOutputs[0]) | ||
| firstInput = input.second; | ||
| if (input.first == "depth") | ||
| depthInput = input.second; | ||
| if (input.first == HdAovTokens->Neye) | ||
| neyeInput = input.second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Observation]
I am guessing this part, where the 3 inputs are collected before calling SetViewportRenderOutput, is not longer relevant since the function calls GetRenderOutput(color/depth/Neye) internally to get each one of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is no longer needed.
source/tasks/copyTask.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad this was removed. Less complexity.
| PXR_NS::TfToken aovName; | ||
| PXR_NS::HgiTextureHandle texture; | ||
| PXR_NS::HdRenderBuffer* buffer; | ||
| std::string rendererName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: use a TfToken for the renderName instead of a string.
Since TfToken creation is somewhat expensive, it could be created once in FramePass::Initialize(), then stored as a FramePass or RenderBufferManager member variable and assigned in the RenderBufferBinding without having to deal with the string representation after FramePass::Initialize().
Comparisons and assignments of TfTokens are cheap, once converted from a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, could be. This is not executed in a deep or large loop so there will be no real performance improvement. I wish the name from Hydra was a token to begin with, or there was a way to get the more accurate name "HdStormRenderDelegate". We could store it in the frame pass, but I would like to make this change in a second PR not this one.
include/hvt/engine/framePass.h
Outdated
| /// Returns the default selection accessor. | ||
| SelectionSettingsProviderWeakPtr GetSelectionSettingsAccessor() const; | ||
|
|
||
| // Returns true if the frame pass should be rendered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be careful Doxygen comments are /// (and not //.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix this.
| { | ||
| PXR_NS::TfToken aovName; | ||
| PXR_NS::HgiTextureHandle texture; | ||
| PXR_NS::HdRenderBuffer* buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PXR_NS::HdRenderBuffer* buffer; -> PXR_NS::HdRenderBuffer* buffer = nullptr;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I will fix.
| if (input.aovName == localOutputs[i]) | ||
| { | ||
| foundInput = input.second; | ||
| inputFound = (rendererName == input.rendererName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the name comparison enough?
The difference is not on the render delegate name but rather on CPU vs. GPU and texture formats. For example, if two different render delegates are both GPU & same texture formats, the code should not add extra copy (ies). I assume that it will be the case between Flash and Storm for example.
| ctx->erase(HdAovTokens->depth); | ||
| ctx->erase(HdxAovTokens->colorIntermediate); | ||
| ctx->erase(HdAovTokens->Neye); | ||
| ctx->erase(msaaToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sebastienberube-adsk It seems that the neye rework remains. That could be a in Q4 dedicated backlog as discussed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't touch Neye. It still needs to be fixed. It should not be on the context. The context is for things that are read and rewritten, not just read.
source/engine/framePass.cpp
Outdated
| } | ||
|
|
||
| hvt::RenderBufferBindings FramePass::GetRenderBufferBindingsForNextPass( | ||
| std::vector<pxr::TfToken> const aovs, bool copyContents) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::vector<pxr::TfToken> const aovs -> std::vector<pxr::TfToken> const& aovs
This submission fixes render buffer handling between renderers and frame passes.
Removes the CopyTask and moves copy functionality to the RenderBufferManager.
Moves AOVInput and RenderTask gathering out of the update function and puts it in the render loop.
Texture output contents are now copied to the next pass buffer inputs between passes, not as part of the pass.
RenderBuffers are reused from the previous pass not the first pass which supports more flexible mixing of renderers.
New render buffers are created if the render switches between passes so renderers are no longer rendering into a render buffer they did not create. RenderBuffers are still reused from previous passes if the renderer does not change.